import * as THREE from 'three';
import Render from "./Render"
import Image2DRenderProxy from './Image2DRenderProxy';

let positionsFrontV3 = [...Array(4)].map((e) => new THREE.Vector3());
let positionsBackV3 = [...Array(4)].map((e) => new THREE.Vector3());

let positionsFrontV2 = [...Array(4)].map((e) => new THREE.Vector2());
let positionsBackV2 = [...Array(4)].map((e) => new THREE.Vector2());

const tempMap = new Map();
let near = 0.01;
let far = 10000;
function createMatrixFromCameraInternal(
  option,
  w,
  h,
) {
  let { fx, fy, cy, cx } = option;
  // return new THREE.Matrix4().set(
  //     2*fx / w,       0,                   1 - 2*cx / w,                 0,
  //     0,           2*fy / h,          2*cy / h - 1,                      0,
  //     0,             0,            (near + far) / (near - far),   2*far*near / (near - far),
  //     0,             0,                      -1.0,                       0);

  return new THREE.Matrix4().set(
      (2 * fx) / w,
      0,
      1 - (2 * cx) / w,
      0,
      0,
      (2 * fy) / h,
      (2 * cy) / h - 1,
      0,
      0,
      0,
      (near + far) / (near - far),
      (2 * far * near) / (near - far),
      0,
      0,
      -1,
      0,
  );
}
function get(type, index = 0) {
  let tempArr = tempMap.get(type);
  if (!tempArr) {
      tempArr = [];
      tempMap.set(type, tempArr);
  }

  let value = tempArr[index];
  if (!value) {
      tempArr[index] = new type();
      value = tempArr[index];
  }

  return value;
}
export default class Image2DRenderView extends Render {
  container;
  // matrix
  proxyOffset = new THREE.Vector2();
  proxyTransformMatrix = new THREE.Matrix4();
  // local matrix
  containerMatrix = new THREE.Matrix4();
  fitMatrix = new THREE.Matrix4();
  transformMatrix = new THREE.Matrix4();
  pointCloud;
  width;
  height;
  // proxy
  proxy;
  clientRect = {};
  // 3d renderer
  clipCamera;
  camera;
  cameraHelper;
  option = {};
  group;
  // project matrix
  matrixExternal = new THREE.Matrix4();
  matrixInternal = new THREE.Matrix4();
  matrix = new THREE.Matrix4();
  // img
  img = null;
  imgSize = new THREE.Vector2(1, 1);
  imgAspectRatio = 1;
  // render config
  lineWidth = 1;
  constructor(container, pointCloud, config) {
    super(config.name || '');
    this.container = container;
    this.pointCloud = pointCloud;

    this.width = this.container.clientWidth || 10;
    this.height = this.container.clientHeight || 10;

    let group = new THREE.Group();
    this.pointCloud.scene.add(group);
    this.group = group;

    if (config.proxy) {
      this.proxy = config.proxy;
    } else {
      this.proxy = new Image2DRenderProxy(pointCloud);
      this.proxy.attach(this.container);
    }

    this.proxy.addView(this);
    this.camera = new THREE.PerspectiveCamera(50, this.width / this.height, 1, 1000);
    // this.camera = new THREE.OrthographicCamera(-10, 10, 10, -10, 0.001, 1000);
    this.group.add(this.camera);

    const helper = new THREE.CameraHelper(this.camera);
    helper.visible = false;
    this.group.add(helper);
    // @ts-ignore
    this.cameraHelper = helper;

    // clip
    this.clipCamera = new THREE.PerspectiveCamera(100, this.width / this.height, 0.01, 100);
  }

  init() {}

  setOptions(option) {
    this.option = option;

    let imgObject = option.imgObject;

    this.img = imgObject;

    this.imgSize.set(imgObject.naturalWidth, imgObject.naturalHeight);
    this.imgAspectRatio = this.imgSize.x / this.imgSize.y;
    this.updateAspectRatioConfig();

    this.matrixInternal.copy(
      createMatrixFromCameraInternal(option.cameraInternal, this.imgSize.x, this.imgSize.y),
    );

    // @ts-ignore
    this.matrixExternal.set(...option.cameraExternal);

    // @ts-ignore
    let matrix = new THREE.Matrix4().set(...option.cameraExternal);
    matrix.premultiply(new THREE.Matrix4().makeScale(1, -1, -1));
    matrix.invert();
    matrix.decompose(this.camera.position, this.camera.quaternion, this.camera.scale);
    this.camera.updateMatrixWorld();

    this.matrix.copy(this.matrixInternal).multiply(this.camera.matrixWorldInverse);

    this.camera.projectionMatrix.copy(this.matrixInternal);
    this.camera.projectionMatrixInverse.copy(this.camera.projectionMatrix).invert();
    // @ts-ignore
    this.cameraHelper.update();

    // clip
    this.clipCamera.position.copy(this.camera.position);
    this.clipCamera.quaternion.copy(this.camera.quaternion);
    this.clipCamera.scale.copy(this.camera.scale);
    this.clipCamera.updateMatrixWorld();
    // this.testFrustum();
    console.log("他来了");
    this.render();
  }

  updateAspectRatioConfig() {
    let { imgSize, width, height, img } = this;

    if (!img) return;

    let scaleX = imgSize.x / width;
    let scaleY = imgSize.y / height;

    let scale = 1;
    let offsetX = 0;
    let offsetY = 0;
    if (scaleX > scaleY) {
        scale = 1 / scaleX;
        offsetY = (height - imgSize.y * scale) / 2;
    } else {
        scale = 1 / scaleY;
        offsetX = (width - imgSize.x * scale) / 2;
    }
    this.fitMatrix.makeScale(scale, scale, 1);

    let translate = get(THREE.Matrix4);
    translate.makeTranslation(offsetX, offsetY, 0);
    this.fitMatrix.premultiply(translate);
  }

  renderFrame() {
    console.log('renderFrame------------------>Image2DRenderView');
    if (!this.isViewRenderable()) return;

    // this.dispatchEvent({ type: Event.RENDER_BEFORE });
    this.proxy.renderN++;

    this.updateSize();
    this.updateTransform();
    this.setViewport();

    this.renderImage();
    // this.renderObjects();

    // this.dispatchEvent({ type: Event.RENDER_AFTER });
  }

  render() {
    if (!this.isEnable()) return;
    console.log("执行了");
    this.proxy.render();
  }

  isViewRenderable() {
    let { clientRect } = this.proxy;
    let rect = this.container.getBoundingClientRect();
    this.clientRect = rect;

    let needRender =
      rect.bottom >= clientRect.top &&
      rect.top <= clientRect.bottom &&
      rect.left <= clientRect.right &&
      rect.right >= clientRect.left;

    return needRender;
  }
}